using DataAnalyticsTools.Core;
using DataAnalyticsTools.Models;
using GrapeCity.Forguncy.Commands;
using GrapeCity.Forguncy.Log;
using GrapeCity.Forguncy.Plugin;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;

namespace DataAnalyticsTools
{
    [Icon("pack://application:,,,/DataAnalyticsTools;component/Resources/Icon.png")]
    [Designer("DataAnalyticsTools.Designer.DataAnalyticsToolsServerCommandDesigner, DataAnalyticsTools")]
    [Category("数据分析")]
    [OrderWeight(700)]
    public class CorrelationMatrix : Command, ICommandExecutableInServerSideAsync, IServerCommandParamGenerator
    {
        [FormulaProperty]
        [DisplayName("样本特征矩阵")]
        [Description("float[][]二维数组，外层为样本，内层为该样本的各维度特征。")]
        public object featuresExpr { get; set; }

        [FormulaProperty]
        [DisplayName("特征名称数组")]
        [Description("string[]一维数组，表示每个特征维度的名称。")]
        public object featureNamesExpr { get; set; }

        [DisplayName("计算方法")]
        public CorrelationMethod Method { get; set; }

        [ResultToProperty]
        [DisplayName("保存相关系数到变量")]
        [Description("相关性在CorrelationCoefficient属性")]
        public string ResultTo { get; set; } = "相关系数矩阵";

        public async Task<ExecuteResult> ExecuteAsync(IServerCommandExecuteContext dataContext)
        {
            var features = await dataContext.EvaluateFormulaAsync(featuresExpr);
            var featureNames = GeneralConvertors.ConvertToStringArray(await dataContext.EvaluateFormulaAsync(featureNamesExpr));

            float[,] result;

            // 执行计算
            if (Method == CorrelationMethod.Spearman)
            {
                result = CorrelationAnalyzer.SpearmanCorrelationMatrix(
                 GeneralConvertors.ConvertToFloatMatrix(features));
            }
            else
            {
                result = CorrelationAnalyzer.PearsonCorrelationMatrix(
                    GeneralConvertors.ConvertToFloatMatrix(features));
            }

            List<CorrelationData> finalResults = [];

            for (int i = 0; i < result.Length; i++)
            {
                for (int j = i + 1; j < result.GetLength(1); j++)
                {
                    if(i == j) continue; // 跳过对角线

                    if(finalResults.Any(d => (d.FeatureIndex1 == i && d.FeatureIndex2 == j) || (d.FeatureIndex1 == j && d.FeatureIndex2 == i)))
                        continue; // 已存在该对

                    finalResults.Add(new CorrelationData
                    {
                        FeatureIndex1 = i,
                        FeatureName1 = featureNames.Length > i ? featureNames[i] : null,
                        FeatureIndex2 = j,
                        FeatureName2 = featureNames.Length > j ? featureNames[j] : null,
                        CorrelationCoefficient = result[i, j]
                    });
                }
            }

            // 按相关系数绝对值降序排列
            dataContext.Parameters[ResultTo] = finalResults.OrderByDescending(d => d.AbsoluteCorrelation);

            return new ExecuteResult();
        }

        public override string ToString()
        {
            if (featuresExpr == null)
            {
                return "计算特征相关系数";
            }
            else
            {
                if (Method == CorrelationMethod.Spearman)
                    return "计算特征相关系数（斯皮尔曼法）：" + ResultTo;
                else
                    return "计算特征相关系数（皮尔逊法）：" + ResultTo;
            }
        }

        public override CommandScope GetCommandScope()
        {
            return CommandScope.ExecutableInServer;
        }

        public IEnumerable<GenerateParam> GetGenerateParams()
        {
            yield return new GenerateListParam()
            {
                ParamName = this.ResultTo,
                Description = "相关系数的计算结果",
                ParamScope = CommandScope.All,
                ItemProperties = new List<string>() {
                    "FeatureIndex1",
                    "FeatureName1",
                    "FeatureIndex2",
                    "FeatureName2",
                    "AbsoluteCorrelation",
                    "CorrelationCoefficient",
                    "CorrelationLevel"
                }
            };
        }

    }

    public class CorrelationData
    {
        public int FeatureIndex1 { get; set; }
        public string FeatureName1 { get; set; }
        public int FeatureIndex2 { get; set; }
        public string FeatureName2 { get; set; }
        public float CorrelationCoefficient { get; set; }
        public float AbsoluteCorrelation => Math.Abs(CorrelationCoefficient);

        public string CorrelationLevel
        {
            get
            {
                var absValue = Math.Abs(CorrelationCoefficient);
                if (absValue >= 0.7) return "极强";
                if (absValue >= 0.5) return "强";
                if (absValue >= 0.3) return "中等";
                if (absValue >= 0.1) return "弱";
                return "极弱或无";
            }
        }
    }

    public enum CorrelationMethod
    {
        [Description("皮尔逊相关系数：线性关系，适用于符合正态分布、无异常值的数据")]
        Pearson,
        [Description("斯皮尔曼相关系数：单调关系，适用于不符合正态分布、存在异常值或重复值的数据")]
        Spearman
    }
}
